unit KRK.Lib.CryptoAPI.SHA512;
{$WEAKPACKAGEUNIT ON}
interface

uses System.Classes
   , System.SysUtils
   , KRK.Lib.Rtl.Win.AdvApi32;

function SHA512Hash(Stream: TStream): TSHA512Hash; overload;
function SHA512Hash(const Bytes: TBytes): TSHA512Hash; overload;
function SHA512Hash(const Str: RawByteString): TSHA512Hash; overload;
function SHA512ToString(const Hash: TSHA512Hash): string;

implementation

uses Winapi.Windows
   , System.Math;

{$WARNINGS OFF}
function SHA512Hash(Stream: TStream): TSHA512Hash;
const
  BuffSize = 1024;
var
  hProv: HCRYPTPROV;
  hHash: HCRYPTHASH;
  Buff: array [0..BuffSize] of Byte;
  BytesLeft: Int64;
  BytesToRead: Integer;
  HashSize: DWORD;
begin
  Win32Check(CryptAcquireContextW(hProv, nil, nil, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
  try
    Win32Check(CryptCreateHash(hProv, CALG_SHA_512, 0, 0, hHash));
    try
      BytesLeft := Stream.Size-Stream.Position;

      while BytesLeft > 0 do
      begin
        BytesToRead := Min(BytesLeft, BuffSize);
        Stream.ReadBuffer(Buff, BytesToRead);
        Win32Check(CryptHashData(hHash, @Buff, BytesToRead, 0));
        dec(BytesLeft, BytesToRead);
      end;

      HashSize := SizeOf(Result);
      Win32Check(CryptGetHashParam(hHash, HP_HASHVAL, @Result, HashSize, 0));
    finally
      Win32Check(CryptDestroyHash(hHash));
    end;
  finally
    Win32Check(CryptReleaseContext(hProv, 0));
  end;
end;
{$WARNINGS ON}
function SHA512Hash(const Str: RawByteString): TSHA512Hash;
var
  Stream: TStringStream;
begin
  Stream := TStringStream.Create(Str);
  try
    Result := SHA512Hash(Stream);
  finally
    Stream.Free;
  end;
end;

function SHA512Hash(const Bytes: TBytes): TSHA512Hash;
var
  Stream: TBytesStream;
begin
  Stream := TBytesStream.Create(Bytes);
  try
    Result := SHA512Hash(Stream);
  finally
    Stream.Free;
  end;
end;

function SHA512ToString(const Hash: TSHA512Hash): string;
begin
  SetLength(Result, SizeOf(Hash) * 2);
  BinToHex(Pointer(@Hash)^, PChar(Result), SizeOf(Hash));
  Result := LowerCase(Result);
end;

end.
